#ifndef __LUAIMPL_H__
#define __LUAIMPL_H__
#include "Lua.h"
#include "LuaClass.h"

namespace LuaStack
{
	
	template<class T>
	struct StackValue
	{
		static bool Check(CLua* L,int index) 
		{
			CHECKSTACK_BEGIN(L,0);
			if(!LuaClass<T>::GetName())
				return false;
			if(!L->IsUserData(index))
				return false;
			if(!L->GetMetatable(index))
				return false;
			do 
			{
				if(L->NewMetatable(LuaClass<T>::GetName()))
					break;
				L->PushValue(-2);
				while(!L->RawEqual(-1,-2))
				{
					L->PushString("__supermet");
					L->RawGet(-2);
					if(L->IsNil(-1))
					{
						L->StackPop(3);
						return false;
					}
					L->Replace(-2);
				}
				L->StackPop(2);
				return true;
			} while (false);
			L->StackPop(1);
			return false;
		}
		static T Get(CLua* L,int Index)
		{
			CHECKSTACK_BEGIN(L,0);
			return *(L->GetUserData<T>(Index));
		}
		static void Push(CLua* L,T t)
		{
			CHECKSTACK_BEGIN(L,0);
			L->PushUserData<T>(t);
		}
	};

	template<class T>
	struct StackValue<T*>
	{
		static bool Check(CLua* L,int index) 
		{
			CHECKSTACK_BEGIN(L,0);
			if(!LuaClass<T>::GetName())
				return false;
			if(!L->IsUserData(index))
				return false;
			if(!L->GetMetatable(index))
				return false;
			do 
			{
				if(L->NewMetatable(LuaClass<T>::GetName()))
					break;
				L->PushValue(-2);
				while(!L->RawEqual(-1,-2))
				{
					L->PushString("__supermet");
					L->RawGet(-2);
					if(L->IsNil(-1))
					{
						L->StackPop(3);
						return false;
					}
					L->Replace(-2);
				}
				L->StackPop(2);
				return true;
			} while (false);
			L->StackPop(1);
			return false;
		}
		static T* Get(CLua* L,int Index)
		{
			CHECKSTACK_BEGIN(L,0);
			if(!L->IsUserData(Index))
				return 0;
			return L->GetUserData<T>(Index);
		}
		static void Push(CLua* L,T* t)
		{
			CHECKSTACK_BEGIN(L,0);
			L->PushUserData<T*>(t);
		}
	};

#define DEFINE_STACKVALUE_NUMBER(T) \
	template<> struct StackValue<T> \
	{ \
		static bool Check(CLua* L,int index) \
		{ \
			return L->IsNumber(index); \
		} \
		static T Get(CLua* L,int index) \
		{ \
			if(!L->IsNumber(index)) \
				return 0; \
			return (T)L->ToNumber(index); \
		} \
		static void Push(CLua* L,T t) \
		{ \
			L->PushNumber(t); \
		} \
	};

	DEFINE_STACKVALUE_NUMBER(char);
	DEFINE_STACKVALUE_NUMBER(short);
	DEFINE_STACKVALUE_NUMBER(long);
	DEFINE_STACKVALUE_NUMBER(int);
	DEFINE_STACKVALUE_NUMBER(float);
	DEFINE_STACKVALUE_NUMBER(double);
	DEFINE_STACKVALUE_NUMBER(unsigned char);
	DEFINE_STACKVALUE_NUMBER(unsigned short);
	DEFINE_STACKVALUE_NUMBER(unsigned long);
	DEFINE_STACKVALUE_NUMBER(unsigned int);

	template<> struct StackValue<const char*>
	{
		static bool Check(CLua* L,int index)
		{
			return L->IsString(index);
		}
		static const char* Get(CLua* L,int index)
		{
			if(!L->IsString(index))
				return 0;
			return L->ToString(index);
		}
		static void Push(CLua* L,const char* t)
		{
			L->PushString(t);
		}
	};

	template<> struct StackValue<bool>
	{
		static bool Check(CLua* L,int index)
		{
			return L->IsBool(index);
		}
		static bool Get(CLua* L,int index)
		{
			if(!L->IsBool(index))
				return false;
			return L->ToBool(index);
		}
		static void Push(CLua* L,bool t)
		{
			L->PushBool(t);
		}
	};

	template<> struct StackValue<lua_CFunction>
	{
		static bool Check(CLua* L,int index)
		{
			return L->IsCFunction(index);
		}
		static lua_CFunction Get(CLua* L,int index)
		{
			if(!L->IsCFunction(index))
				return 0;
			return L->ToCFunction(index);
		}
		static void Push(CLua* L,lua_CFunction t)
		{
			L->PushCFunction(t);
		}
	};
}


#endif
